import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import promptAction from '@ohos.promptAction';
import router from '@ohos.router';
import window from '@ohos.window';
import Logger from '../common/utils/Logger';

const CALL_METHOD_PLAY: string = 'Play';
const CALL_METHOD_STOP: string = 'Stop';

/**
 * 调用 formProvider.updateForm 更新卡片内容
 * @param data 数据
 */
function doUpdateForm(data) {
  // 生成绑定卡片数据
  let formData = formBindingData.createFormBindingData(data)
  // 更新卡片数据
  formProvider.updateForm(data.formId, formData).then((res) => {
    Logger.info('EntryAbility', 'updateForm success.' + JSON.stringify(res));
  }).catch((error) => {
    Logger.info('EntryAbility', 'updateForm failed:' + JSON.stringify(error));
  })
}

/**
 * 卡片调用  postCardAction(this, { "action": "call",...,"params": {'method': 'Play', 'arg1': 'value1',...}}); 时相应的函数
 * @param data 传入的参数
 * @returns 返回值
 */
function onPlay(data) {
  // 获取call事件中传递的所有参数
  let params = JSON.parse(data.readString())
  let text = 'called Play, param:' + JSON.stringify(params)
  Logger.info('EntryAbility', text);
  if (params.formId != undefined) {
    let formData = {
      'formId': params.formId,
      'title': 'call Play 事件监听更新数据',
      "text": text,
      'loaded': true
    };
    // 更新卡片数据
    doUpdateForm(formData)
  }
  return null;
}

/**
 * 卡片调用  postCardAction(this, { "action": "call",...,"params": {'method': 'Stop', 'arg1': 'value1',...}}); 时相应的函数
 * @param data 传入的参数
 * @returns 返回值
 */
function onStop(data) {
  let params = JSON.parse(data.readString())
  let text = 'called Play, param:' + JSON.stringify(params)
  Logger.info('EntryAbility', text);
  if (params?.formId != undefined) {
    let formData = {
      'formId': params.formId,
      'title': 'call Stop 事件监听更新数据',
      "text": text,
      'loaded': true
    };
    // 更新卡片数据
    doUpdateForm(formData)
  }
  return null;
}

/**
 * UIAbility 组件是一种包含UI界面的应用组件，主要用于和用户交互：
 *  - UIAbility组件是系统调度的基本单元，为应用提供绘制界面的窗口。
 *  - 一个UIAbility组件中可以通过多个页面来实现一个功能模块。每一个UIAbility组件实例，都对应于一个最近任务列表中的任务。
 *
 * 声明配置 UIAbility：
 *  为使应用能够正常使用UIAbility，需要在module.json5配置文件的abilities标签中声明UIAbility的名称、入口、标签等相关信息。
 * ```json
 *  // module.json5
 *  {
 *    "module": {
 *      // ...
 *      "abilities": [
 *        {
 *          "name": "EntryAbility", // UIAbility组件的名称
 *          "srcEntrance": "./ets/entryability/EntryAbility.ts", // UIAbility组件的代码路径
 *          "description": "$string:EntryAbility_desc", // UIAbility组件的描述信息
 *          "icon": "$media:icon", // UIAbility组件的图标
 *          "label": "$string:EntryAbility_label", // UIAbility组件的标签
 *          "startWindowIcon": "$media:icon", // UIAbility组件启动页面图标资源文件的索引
 *          "startWindowBackground": "$color:start_window_background", // UIAbility组件启动页面背景颜色资源文件的索引
 *          // ...
 *        }
 *      ]
 *    }
 *  }
 * ```
 *
 * UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态:
 *  【Create】：UIAbility实例创建完成时触发，系统会调用 onCreate() 回调。
 *    在 onCreate()之后，FonForeground()之前，系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调，可以在该回调中设置UI界面加载、设置WindowStage的事件订阅。
 *  【Foreground】：UIAbility实例切换至前台时触发，调用 onForeground() 回调
 *  【Background】：UIAbility实例切换至后台时触发，调用 onForeground() 回调
 *    在UIAbility实例销毁（onDestroy回调）之前，则会先进入onWindowStageDestroy()回调，可以在该回调中释放UI界面资源。
 *  【Destroy】：UIAbility实例销毁时触发。可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作。
 */
export default class EntryAbility extends UIAbility {
  readonly TAG: string = 'EntryAbility';

  user = {
    userId: 1,
    userName: '管理员',
    account: 'admin'
  };
  // 当前Ability创建后常见的WindowStage实例对象，在onWindowStageCreate生命周期回调中赋值
  windowStage: window.WindowStage;
  // 定义全局的 LocalStorage 对象
  storage: LocalStorage = new LocalStorage(this.user);

  /**
   * 一、Create状态为在应用加载过程中，UIAbility实例创建完成时触发，系统会调用onCreate()回调。
   * 可以在该回调中进行应用初始化操作，例如变量定义资源加载等，用于后续的UI界面展示。
   * @param want 通过通知消息由应用组件管理服务AMS（Ability Manager Service）触发启动应用时传递的用户期望的行为意图
   * @param launchParam
   */
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 获取应用的上下文对象：提供了订阅应用内Ability的生命周期的变化、订阅系统内存变化和订阅应用内系统环境的变化的能力
    let applicationContext = this.context.getApplicationContext();
    //let stageContext = this.context.
    // EventHub事件监听
    this.context.eventHub.on('EventHubTest', (data: string) => {
      promptAction.showToast({
        message: data
      });
    });
    // 在全局对象 globalThis上绑定属性，可以实现UIAbility组件与UI之间的数据同步。
    globalThis.context = this.context;
    globalThis.user = this.user;
    globalThis.filesDir = this.context.filesDir;

    Logger.info(this.TAG, 'Ability onCreate');
    Logger.info(this.TAG, `Ability onCreate globalThis -> ${globalThis.filesDir}`);
    Logger.info(this.TAG, `Ability onCreate -> want: ${JSON.stringify(want)}`);
    Logger.info(this.TAG, `Ability onCreate -> launchParam: ${JSON.stringify(launchParam)}`);

    try {
      // 如果UIAbility第一次启动，在收到 postCardAction call事件后会触发onCreate生命周期回调
      this.callee.on(CALL_METHOD_PLAY, onPlay);
      this.callee.on(CALL_METHOD_STOP, onStop);
    } catch (error) {
      Logger.error(this.TAG, 'callee监听事件失败，error:' + JSON.stringify(error));
    }
  }

  /**
   * 二、onCreate 事件之后 -> WindowStage 实例创建完成之后执行  -> 设置应用主窗口属性及目标页面
   * UIAbility 实例创建完成之后，在进入Foreground之前，系统会创建一个 WindowStage，相当于一个呈现UI的视窗。
   * WindowStage 创建完成后会进入 onWindowStageCreate() 回调，可以在该回调中设置UI界面加载、设置WindowStage的事件订阅。
   * 在 onWindowStageCreate() 回调中通过 windowStage.loadContent() 方法设置应用要加载的页面，并根据需要订阅 WindowStage 的事件（获焦/失焦、可见/不可见）。
   * @param windowStage WindowStage 对象实例
   */
  onWindowStageCreate(windowStage: window.WindowStage) {
    this.windowStage = windowStage;
    globalThis.windowStage = windowStage;
    Logger.info(this.TAG, 'Ability onWindowStageCreate');
    let windowClass = null;
    // 1.获取主窗口，设置设置应用主窗口属性，可设置主窗口的背景色、亮度值、是否可触等多个属性
    windowStage.getMainWindow().then(data => {
      Logger.info(this.TAG, 'Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));
      windowClass = data;
      // 设置主窗口背景色
      windowClass.setWindowBackgroundColor('#1652FF');

      // 设置窗口是否全屏展示
      windowClass.setWindowLayoutFullScreen(false).then(() => {
        Logger.info(this.TAG, 'Succeeded in setWindowLayoutFullScreen false.');
      }).catch(err => {
        Logger.error(this.TAG, 'Failed to setWindowLayoutFullScreen. Cause', err);
        return;
      });

      // 设置主窗口属性。以设置"是否可触"属性为例。
      windowClass.setWindowTouchable(true).then(() => {
        Logger.info(this.TAG, 'Succeeded in setting the window to be touchable.');
      }).catch(err => {
        Logger.error(this.TAG, 'Failed to set the window to be touchable. Cause', err);
        return;
      });

      // 设置窗口大小改变事件监听
      windowClass.on('windowSizeChange', size => {
        Logger.info(this.TAG, 'window size changed. size:' + JSON.stringify(size));
      });

      // 实现沉浸式效果：设置导航栏、状态栏不显示。
      let names = []; // Array<'status'|'navigation'> -> 需全部显示，该参数设置为['status', 'navigation']；
      windowClass.setWindowSystemBarEnable(names, (err) => {
        if (err.code) {
          Logger.error(this.TAG, 'Failed to set the system bar to be visible. Cause:', err);
          return;
        }
        Logger.info(this.TAG, 'Succeeded in setting the system bar to be visible.');
      });
    }).catch(err => {
      Logger.error(this.TAG, 'Failed to obtain the main window. Cause', err);
      return;
    });

    // 2.设置主窗口属性目标UI界面加载
    // 注意：可以根据UIAbility启动时的参数 launchWant 来设置外部应用期望拉起应用时的行为，比如：跳转到指定页面
    if (this.launchWant?.uri) {
      Logger.info(this.TAG, 'Succeeded in loading the launchWant :' + JSON.stringify(this.launchWant) ?? '');
      windowStage.loadContent(this.launchWant.uri, this.storage, (err, data) => {
        if (err.code) {
          Logger.error(this.TAG, 'Failed to load the content. Cause: ' + JSON.stringify(err) ?? '');
          return;
        }
        Logger.info(this.TAG, 'Succeeded in loading the content. Data:' + JSON.stringify(data) ?? '');
      });
    } else {
      // 传入 LocalStorage 对象，UI页面中可以使用  let storage = LocalStorage.GetShared() 获取该对象
      windowStage.loadContent('pages/Index', this.storage, (err, data) => {
        if (err.code) {
          Logger.error(this.TAG, 'Failed to load the content. Cause: ' + JSON.stringify(err) ?? '');
          return;
        }
        Logger.info(this.TAG, 'Succeeded in loading the content. Data:' + JSON.stringify(data) ?? '');
      });
    }
  }

  /**
   * 三、在UIAbility实例切换至前台时触发，此时状态为 Foreground
   * 申请系统需要的资源，或者重新申请在onBackground中释放的资源
   */
  onForeground() {
    // Ability has brought to foreground
    Logger.info(this.TAG, 'Ability onForeground');
  }

  /**
   * 四、在UIAbility实例切换至后台时触发，此时状态为 Background
   * 释放UI界面不可见时无用的资源，或者在此回调中执行较为耗时的操作
   */
  onBackground() {
    // Ability has back to background
    Logger.info(this.TAG, 'Ability onBackground');
  }

  /**
   * 五、在UIAbility实例销毁前触发：先销毁 WindowStage 对象
   * 可以在该回调中释放UI界面资源。例如在onWindowStageDestroy()中注销获焦/失焦等WindowStage事件。
   */
  onWindowStageDestroy() {
    // Main window is destroyed, release UI related resources
    Logger.info(this.TAG, 'Ability onWindowStageDestroy');
  }

  /**
   * 六、在UIAbility实例销毁时触发，此时为 Destroy。可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作
   */
  onDestroy() {
    Logger.info(this.TAG, 'Ability onDestroy');
    // 取消EventHub事件监听
    this.context.eventHub.off('EventHubTest');

    // 进程退出时，解除监听
    try {
      this.callee.off(CALL_METHOD_PLAY);
      this.callee.off(CALL_METHOD_STOP);
    } catch (error) {
      Logger.error(this.TAG, 'callee.off register failed with error. Cause: ' + JSON.stringify(error));
    }
  }

  /**
   * 七、当传入新的Want，ability再次被拉起时会回调执行该方法
   * @param want Want类型参数，如ability名称，包名等
   * @param launchParams UIAbility启动的原因、上次异常退出的原因信息
   */
  onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam) {
    if (want) {
      Logger.info(this.TAG, `onNewWant -> want: ${JSON.stringify(want)}`);
      Logger.info(this.TAG, `onNewWant -> launchParams: ${JSON.stringify(launchParams)}`);

      //跳转地址
      if (want.uri && this.windowStage) {
        // 重新加载窗口内容
        this.windowStage.loadContent(want.uri, this.storage, (err, data) => {
          if (err.code) {
            Logger.error(this.TAG, 'Failed to load the content. Cause: ' + JSON.stringify(err) ?? '');
            return;
          }
          Logger.info(this.TAG, 'Succeeded in loading the content. Data:' + JSON.stringify(data) ?? '');
        });
      }
    }
  }
};
